'use strict';

var _ = require('lodash');
var errCode = require('../../conf/errorCode');
var dao = require('../database/dao');
var crypto = require('crypto');
let Duplex = require('stream').Duplex,
  moment = require('moment');

const CREATED_TIMESTAMP_TYPE = 'createdAt';
const RUNNING_TIMESTAMP_TYPE = 'runningAt';
const DONE_TIMESTAMP_TYPE = 'doneAt';
const DATE_FORMAT = 'YYYY-MM-DD';
const IPV4_REGEXP = /^(::ffff:)?((((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]\d)|(\d)).){3,3}((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]\d)|([\d])))$/i;
const KEY = '^&YGB*8^'; //加密的秘钥

moment.locale('zh-cn');

var compactObject = (exports.compactObject = function(obj) {
  return _.pickBy(obj, v => v != null);
});

var deepCompactObject = (exports.deepCompactObject = function(obj) {
  var comObj = compactObject(obj);
  _.forOwn(comObj, (v, k) => {
    if (_.isObject(v)) comObj[k] = deepCompactObject(v);
  });
  return comObj;
});

var genTimestampFunc = (exports.genTimestampFunc = function(tsType) {
  return obj => {
    if (obj) {
      obj.ts = obj.ts || {};
      obj.ts[tsType] = Date.now();
    }
  };
});

var retTimestamps = (exports.retTimestamps = function(obj) {
  if (obj && obj.ts) {
    var ts = obj.ts;
    ts['cost(ms)'] = ts.doneAt - ts.runningAt;
    return ts;
  }
  return {};
});

var setCreatedAt = (exports.setCreatedAt = genTimestampFunc(
  CREATED_TIMESTAMP_TYPE,
));
var setRunningAt = (exports.setRunningAt = genTimestampFunc(
  RUNNING_TIMESTAMP_TYPE,
));
var setDoneAt = (exports.setDoneAt = genTimestampFunc(DONE_TIMESTAMP_TYPE));

var hideProperty = (exports.hideProperty = function(obj, prop) {
  Object.defineProperty(obj, prop, {enumerable: false, __proto__: null});
});

var getReqRemoteIp = (exports.getReqRemoteIp = function(req) {
  return ((
    (req.headers['x-forwarded-for'] || '').split(',')[0] || req.ip
  ).match(IPV4_REGEXP) || [])[2];
});

var isNonemptyStr = (exports.isNonemptyStr = function(str) {
  return _.isString(str) && str.length > 0;
});

var isToday = (exports.isToday = function(theDate) {
  var date = new Date(); //当前时间
  var today = new Date(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
  ).getTime(); //今天凌晨
  var tommorow = new Date(today + 24 * 3600 * 1000).getTime();
  return theDate.getTime() > today && tommorow >= theDate.getTime();
});
var formatTime = (exports.formatTime = function(time) {
  if (isToday(time)) {
    return moment(time).fromNow();
  } else {
    return moment(time).format(DATE_FORMAT);
  }
});

var isNullOrEmptyStr = (exports.isNullOrEmptyStr = function(str) {
  return str == null || str == '';
});
var isNullOrEmptyObj = (exports.isNullOrEmptyObj = function(str) {
  return !str || (_.isObject(str) && _.keys(str).length == 0);
});
var sendRes = (exports.sendRes = function(req, res, msg) {
  //res.header('Access-Control-Allow-Origin', '*');
  //res.header('Access-Control-Allow-Headers', 'X-Requested-With');
  //res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
  res.header('Content-Type', 'application/json;charset=utf-8');
  if (req.token) {
    //如果有token存在，标识token即将过期
    res.header('x-access-token', req.token);
  }
  !!req.query.callback ? res.jsonp(msg) : res.json(msg);
});

var getHandle = (exports.getHandle = function(req, res, apiNo) {
  return (err, _rel) => {
    if (err) {
      sendRes(req, res, genCodeObj(apiNo, err.errNo, err.extraMsgs));
    } else {
      sendRes(
        req,
        res,
        _.merge(
          {
            data: _rel,
          },
          genNoErrorCodeObj(),
        ),
      );
    }
  };
});

var genCode = (exports.genCode = errCode.genCode);

var transErrNo = (exports.transErrNo = errCode.transErrNo);

var genCodeObj = (exports.genCodeObj = function(apiNo, errNo, extraMsgs) {
  var msgs = transErrNo(errNo);
  if (extraMsgs != null) msgs = `${msgs}[${extraMsgs}]`;
  return {code: genCode(apiNo || errCode.ANY_API, errNo), msgs: msgs}; // 之所以使用errCode.ANY_API, 仅为防止code为null...
});

const NO_ERROR_OBJ = Object.freeze(
  genCodeObj(errCode.ANY_API, errCode.NO_ERROR),
);
var genNoErrorCodeObj = (exports.genNoErrorCodeObj = function() {
  return NO_ERROR_OBJ;
});

var firstKeyOfValue = (exports.firstKeyOfValue = function(obj, keys, value) {
  var key;
  return _.some(keys, k => {
    key = k;
    return obj[k] == value;
  })
    ? key
    : null;
});
var firstKeyOfNullValues = (exports.firstKeyOfNullValues = function(obj, keys) {
  var key;
  return _.some(keys, k => {
    key = k;
    return !obj[k];
  })
    ? key
    : null;
});

var firstKeyOfNull = (exports.firstKeyOfNull = function(obj, keys) {
  return firstKeyOfNullValues(obj, keys);
});
//map:
//{
//  k1: <boolean: function(v1){}>,
//}
//return: <null> || <string: key_name>
var findInvalidKeyBy = (exports.findInvalidKeyBy = function(obj, map) {
  var key = null;
  _.every(Object.keys(map), k => {
    var ret = map[k](obj[k]);
    if (_.isBoolean(ret)) {
      if (ret) return true;
      key = k;
      return false;
    } else {
      key = ret;
      return false;
    }
  });
  return key;
});

// 将`obj`中`keys`对应的value, 转换为integer.
// 全部成功后, 返回null; 否则中途停止, 并返回失败的那个key的名字
// allowNull: <bool: 是否允许null值通过校验>
var tryParseIntForKeys = (exports.tryParseIntForKeys = function(
  obj,
  keys,
  allowNull,
) {
  var key = null;
  _.every(keys, k => {
    if (allowNull && obj[k] == null) {
      return true;
    } else if (isIntegerLike(obj[k])) {
      obj[k] = _.toInteger(obj[k]);
      return true;
    } else {
      key = k;
      return false;
    }
  });
  return key;
});

// 跟`tryParseIntForKeys`的区别有二:
// 1. parse失败不会返回错误信息
// 2. 会parse `keys`中指定的所有key, 不会中途停止
var parseIntForKeys = (exports.parseIntForKeys = function(obj, keys) {
  _.each(keys, k => {
    obj[k] = _.toInteger(obj[k]);
  });
});

var hasNullForKeys = (exports.hasNullForKeys = function(obj, keys) {
  var k = firstKeyOfValue(obj, keys, null);
  if (k == null) return false;
  return true;
});

var isNumberLike = (exports.isNumberLike = function(num) {
  return (_.isString(num) || _.isNumber(num)) && !_.isNaN(_.toNumber(num));
});

var isIntegerLike = (exports.isIntegerLike = function(num) {
  return (_.isString(num) || _.isNumber(num)) && _.isInteger(_.toNumber(num));
});

var isSubsetOf = (exports.isSubsetOf = function(arr1, arr2) {
  //1是2的子集
  var inter = _.intersection(arr1, arr2);
  return inter.length == arr1.length;
});

var tryParseJSON = (exports.tryParseJSON = function(str) {
  try {
    return JSON.parse(str);
  } catch (e) {}
  return null;
});

// [page, size] to [offset, limit]
var ps2ol = (exports.ps2ol = function(page, size) {
  return [(page - 1) * size, size];
});

var defer = (exports.defer = function() {
  let def = {};
  let promise = new Promise((resolve, reject) => {
    def = {resolve, reject};
  });
  def.promise = promise;
  return def;
});

var genSortNum = (exports.genSortNum = function(num) {
  return parseInt(
    num
      .split('.')
      .map(key => {
        return key < 10 ? '0' + key : key;
      })
      .join(''),
  );
});

var genVersionBySortNum = (exports.genVersionBySortNum = function(num) {
  let sortNum = num,
    version = '',
    fenmu = 100;
  //处理两次，获取前四位数字
  for (let i = 0; i < 2; i++) {
    let yushu = sortNum % fenmu;
    version = '.' + yushu + version;
    sortNum = Math.floor(sortNum / fenmu);
  }
  return (version = num < 10000 ? '0' + version : sortNum + version);
});

var sqlArrMapToTrans = (exports.sqlArrMapToTrans = function(sqlArr) {
  sqlArr.map((i, index) => {
    sqlArr[index] = {str: i};
  });
});

var genPassWd = (exports.genPassWd = function(passWd) {
  let cipher = crypto.createCipher('aes-128-ecb', KEY, ''),
    crypted = cipher.update(passWd, 'utf8', 'hex');
  crypted += cipher.final('hex');
  return crypted;
});

var bufferToStream = (exports.bufferToStream = function(buffer) {
  let stream = new Duplex();
  stream.push(buffer);
  stream.push(null);
  return stream;
});

var buildRandomMsgCode = (exports.buildRandomMsgCode = function() {
  let code = [];
  for (let i = 0; i < 6; i++) {
    let random = Math.floor(Math.random() * 10);
    code.push(random);
  }
  return code.join('');
});

var promiseify = (exports.promiseify = function(method) {
  return function(ctx) {
    let args = Array.prototype.slice.call(arguments);

    return new Promise((resolve, reject) => {
      let cb = function(err, result) {
        if (err) {
          return reject(err);
        }

        return resolve(result);
      };

      args.push(cb);
      method.apply(ctx, args);
    });
  };
});

